﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using DTcms.Common;
using DTcms.Cache.Factory;

namespace DTcms.Web.Mvc.UI
{
    /// <summary>
    /// DTcms的HttpModule类
    /// </summary>
    public class HttpModule : IHttpModule
    {
        /// <summary>
        /// 实现接口的Init方法
        /// </summary>
        /// <param name="context"></param>
        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(ReUrl_BeginRequest);
        }

        /// <summary>
        /// 实现接口的Dispose方法
        /// </summary>
        public void Dispose() { }

        #region 页面请求事件处理===================================
        /// <summary>
        /// 页面请求事件处理
        /// </summary>
        /// <param name="sender">事件的源</param>
        /// <param name="e">包含事件数据的 EventArgs</param>
        private void ReUrl_BeginRequest(object sender, EventArgs e)
        {
            string result = string.Empty;
            HttpContext context = ((HttpApplication)sender).Context;
            Model.siteconfig siteConfig = new BLL.siteconfig().loadConfig(); //获得站点配置信息
            string requestPath = context.Request.Path.ToLower();//获得当前页面(含目录)

            //如果虚拟目录(不含安装目录)与站点根目录名相同则不需要重写
            if (IsDirExist(DTKeys.CACHE_SITE_DIRECTORY, siteConfig.webpath, siteConfig.webpath, requestPath))
            {
                return;
            }
            string requestParam = HttpContext.Current.Request.Url.Query;  //页面参数
            string requestDomain = context.Request.Url.Authority.ToLower();//获得当前域名(含端口号)
            string sitePath = GetSitePath(siteConfig.webpath, requestPath, requestDomain);//获取当前站点目录
            string requestPage = CutStringPath(siteConfig.webpath, sitePath, requestPath);//截取除安装、站点目录部分
            if (!string.IsNullOrEmpty(requestParam) && requestParam.StartsWith("?"))
            {
                requestParam = requestParam.Substring(1);
            }
            if (requestPage == "/")//域名访问 直接跳转到index
            {
                requestPage = "/index.html";
            }
            var allUrls = SiteUrls.GetEffective(siteConfig.webpath, sitePath);
            if (allUrls == null)
            {
                return;
            }
            //遍历URL字典
            foreach (Model.url_rewrite model in allUrls)
            {
                //如果没有重写表达式则不需要重写
                if (model.url_rewrite_items.Count == 0 && requestPath.LastIndexOf("/") == 0 && Utils.GetUrlExtension(model.page, siteConfig.staticextension) == requestPath.Substring(requestPath.LastIndexOf("/") + 1))
                {
                    //映射到站点目录
                    result = siteConfig.webpath + model.name;
                    context.RewritePath(result);
                    return;
                }

                //遍历URL字典的子节点
                foreach (Model.url_rewrite_item item in model.url_rewrite_items)
                {
                    string newPattern = Utils.GetUrlExtension(item.pattern, siteConfig.staticextension); //替换扩展名

                    //如果与URL节点匹配则重写
                    if (Regex.IsMatch(requestPage, string.Format("^/{0}$", newPattern), RegexOptions.None | RegexOptions.IgnoreCase)
                            || (model.page == "index.cshtml" && Regex.IsMatch(requestPage, string.Format("^/{0}$", item.pattern), RegexOptions.None | RegexOptions.IgnoreCase)))
                    {
                        //如果开启生成静态、不是移动站点且是频道页或首页,则映射重写到HTML目录
                        if (siteConfig.staticstatus == 2 && (model.channel.Length > 0 || model.page.ToLower() == "index.cshtml")) //频道页
                        {
                            result = siteConfig.webpath + DTKeys.DIRECTORY_REWRITE_HTML + "/" + sitePath + Utils.GetUrlExtension(requestPage, siteConfig.staticextension, true);
                            context.RewritePath(result);
                            return;
                        }
                        else
                        {
                            //其它
                            string queryString = Regex.Replace(requestPage, string.Format("/{0}", newPattern), item.querystring, RegexOptions.None | RegexOptions.IgnoreCase);
                            if (!string.IsNullOrEmpty(requestParam))
                            {
                                if (!string.IsNullOrEmpty(queryString))
                                {
                                    queryString += "&" + requestParam;
                                }
                                else
                                {
                                    queryString = requestParam;
                                }
                            }
                            result = siteConfig.webpath + model.name;
                            //新增 关于开启单个页面的静态 
                            if (model.is_builder && siteConfig.staticstatus == 3)//启用局部静态+URL
                            {
                                result = siteConfig.webpath + DTKeys.DIRECTORY_REWRITE_HTML + "/" + sitePath + Utils.GetUrlExtension(requestPage, siteConfig.staticextension, true);
                            }
                            context.RewritePath(result, string.Empty, queryString);
                            return;
                        }
                    }
                }
            }
        }
        #endregion

        #region 辅助方法(私有)=====================================
        /// <summary>
        /// 重写路径,主要处理参数
        /// </summary>
        /// <param name="context"></param>
        /// <param name="sendToUrl"></param>
        protected void RewritePath(HttpContext context, string sendToUrl)
        {
            if (context.Request.QueryString.Count > 0)
            {
                if (sendToUrl.IndexOf('?') != -1)
                {
                    sendToUrl += "&" + context.Request.QueryString.ToString();
                }
                else
                {
                    sendToUrl += "?" + context.Request.QueryString.ToString();
                }
            }
            string queryString = String.Empty;
            string sendToUrlLessQString = sendToUrl;
            if (sendToUrl.IndexOf('?') > 0)
            {
                sendToUrlLessQString = sendToUrl.Substring(0, sendToUrl.IndexOf('?'));
                queryString = sendToUrl.Substring(sendToUrl.IndexOf('?') + 1);
            }
            context.RewritePath(sendToUrlLessQString, String.Empty, queryString);
        }

        /// <summary>
        /// 处理各种路径
        /// </summary>
        /// <param name="appPath"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        private string ResolveUrl(string appPath, string url)
        {
            //   return url;
            if (url.Length == 0 || url[0] != '~')
                return url;		// there is no ~ in the first character position, just return the url
            else
            {
                if (url.Length == 1)
                    return appPath;  // there is just the ~ in the URL, return the appPath
                if (url[1] == '/' || url[1] == '\\')
                {
                    // url looks like ~/ or ~\
                    if (appPath.Length > 1)
                        return appPath + "/" + url.Substring(2);
                    else
                        return "/" + url.Substring(2);
                }
                else
                {
                    // url looks like ~something
                    if (appPath.Length > 1)
                        return appPath + "/" + url.Substring(1);
                    else
                        return appPath + url.Substring(1);
                }
            }
        }

        /// <summary>
        /// 获取URL的虚拟目录(除安装目录)
        /// </summary>
        /// <param name="webPath">网站安装目录</param>
        /// <param name="requestPath">当前页面，包含目录</param>
        /// <returns>String</returns>
        private string GetFirstPath(string webPath, string requestPath)
        {
            if (requestPath.StartsWith(webPath))
            {
                string tempStr = requestPath.Substring(webPath.Length);
                if (tempStr.IndexOf("/") > 0)
                {
                    return tempStr.Substring(0, tempStr.IndexOf("/")).ToLower();
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// 获取当前域名包含的站点目录
        /// </summary>
        /// <param name="requestDomain">获取的域名(含端口号)</param>
        /// <returns>String</returns>
        private string GetCurrDomainPath(string requestDomain)
        {
            //当前域名是否存在于站点目录列表
            if (SiteDomains.GetSiteDomains().Paths.ContainsValue(requestDomain))
            {
                string sitePath = SiteDomains.GetSiteDomains().Domains[requestDomain];
                //如果存在，检查是否默认站点，是则还需检查虚拟目录部分
                if (sitePath != SiteDomains.GetSiteDomains().DefaultPath)
                {
                    return sitePath;
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// 获取当前页面包含的站点目录
        /// </summary>
        /// <param name="webPath">网站安装目录</param>
        /// <param name="requestPath">获取的页面，包含目录</param>
        /// <returns>String</returns>
        private string GetCurrPagePath(string webPath, string requestPath)
        {
            //获取URL的虚拟目录(除安装目录)
            string requestFirstPath = GetFirstPath(webPath, requestPath);
            if (requestFirstPath != string.Empty && SiteDomains.GetSiteDomains().Paths.ContainsKey(requestFirstPath))
            {
                return requestFirstPath;
            }
            return string.Empty;
        }

        /// <summary>
        /// 获取站点的目录
        /// </summary>
        /// <param name="webPath">网站安装目录</param>
        /// <param name="requestPath">获取的页面，包含目录</param>
        /// <param name="requestDomain">获取的域名(含端口号)</param>
        /// <returns>String</returns>
        private string GetSitePath(string webPath, string requestPath, string requestDomain)
        {
            //获取当前域名包含的站点目录
            string domainPath = GetCurrDomainPath(requestDomain);
            if (domainPath != string.Empty)
            {
                return domainPath;
            }
            // 获取当前页面包含的站点目录
            string pagePath = GetCurrPagePath(webPath, requestPath);
            if (pagePath != string.Empty)
            {
                return pagePath;
            }
            return SiteDomains.GetSiteDomains().DefaultPath;
        }

        /// <summary>
        /// 遍历指定路径目录，如果缓存存在则直接返回
        /// </summary>
        /// <param name="cacheKey">缓存KEY</param>
        /// <param name="dirPath">指定路径</param>
        /// <returns>ArrayList</returns>
        private ArrayList GetSiteDirs(string cacheKey, string dirPath)
        {
            ArrayList _cache = CacheFactory.Cache().GetCache<ArrayList>(cacheKey);//从续存中取
            if (_cache == null)
            {
                _cache = new ArrayList();
                DirectoryInfo dirInfo = new DirectoryInfo(Utils.GetMapPath(dirPath));
                foreach (DirectoryInfo dir in dirInfo.GetDirectories())
                {
                    _cache.Add(dir.Name.ToLower());
                }
                CacheFactory.Cache().WriteCache(_cache, cacheKey, 2); //存入续存，弹性2分钟
            }
            return _cache;
        }

        /// <summary>
        /// 遍历指定路径的子目录，检查是否匹配
        /// </summary>
        /// <param name="cacheKey">缓存KEY</param>
        /// <param name="webPath">网站安装目录，以“/”结尾</param>
        /// <param name="dirPath">指定的路径，以“/”结尾</param>
        /// <param name="requestPath">获取的URL全路径</param>
        /// <returns>布尔值</returns>
        private bool IsDirExist(string cacheKey, string webPath, string dirPath, string requestPath)
        {
            ArrayList list = GetSiteDirs(cacheKey, dirPath);//取得所有目录
            string requestFirstPath = string.Empty;//获得当前页面除站点安装目录的虚拟目录名称
            string tempStr = string.Empty;//临时变量
            if (requestPath.StartsWith(webPath))
            {
                tempStr = requestPath.Substring(webPath.Length);
                if (tempStr.IndexOf("/") > 0)
                {
                    requestFirstPath = tempStr.Substring(0, tempStr.IndexOf("/"));
                }
            }
            if (requestFirstPath.Length > 0 && list.Contains(requestFirstPath.ToLower()))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 截取安装目录和站点目录部分
        /// </summary>
        /// <param name="webPath">站点安装目录</param>
        /// <param name="sitePath">站点目录</param>
        /// <param name="requestPath">当前页面路径</param>
        /// <returns>String</returns>
        private string CutStringPath(string webPath, string sitePath, string requestPath)
        {
            if (requestPath.StartsWith(webPath))
            {
                requestPath = requestPath.Substring(webPath.Length);
            }
            sitePath += "/";
            if (requestPath.StartsWith(sitePath))
            {
                requestPath = requestPath.Substring(sitePath.Length);
            }
            return "/" + requestPath;
        }
        #endregion
    }

    #region 站点URL字典信息类===================================
    /// <summary>
    /// 站点伪Url信息类
    /// </summary>
    public class SiteUrls
    {
        //属性声明
        private static object lockHelper = new object();
        private static volatile SiteUrls instance = null;
        private List<Model.url_rewrite> _urls;
        public List<Model.url_rewrite> Urls
        {
            get { return _urls; }
            set { _urls = value; }
        }
        //构造函数
        private SiteUrls()
        {
            Urls = new List<Model.url_rewrite>();
            BLL.url_rewrite bll = new BLL.url_rewrite();
            List<Model.url_rewrite> ls = bll.GetListAll();
            foreach (Model.url_rewrite model in ls)
            {
                foreach (Model.url_rewrite_item item in model.url_rewrite_items)
                {
                    item.querystring = item.querystring.Replace("^", "&");
                }
                Urls.Add(model);
            }
        }

        //返回URL字典
        public static SiteUrls GetUrls()
        {
            SiteUrls _cache = CacheFactory.Cache().GetCache<SiteUrls>(DTKeys.CACHE_SITE_HTTP_MODULE);
            if (_cache == null)
            {
                lock (lockHelper)
                {
                    _cache = CacheFactory.Cache().GetCache<SiteUrls>(DTKeys.CACHE_SITE_HTTP_MODULE);
                    if (_cache == null)
                    {
                        instance = new SiteUrls();
                        CacheFactory.Cache().WriteCache(instance, DTKeys.CACHE_SITE_HTTP_MODULE);
                    }
                }
            }
            return instance;
        }

        /// <summary>
        /// 获取当前网站有效的URL
        /// </summary>
        /// <param name="dirPath">指定路径</param>
        /// <returns>ArrayList</returns>
        public static List<Model.url_rewrite> GetEffective(string webpath, string sitePath)
        {
            List<Model.url_rewrite> list = CacheFactory.Cache().GetCache<List<Model.url_rewrite>>(DTKeys.CACHE_SITE_EFFECTIVE_URLS + "_" + sitePath); //从续存中取
            if (list == null)
            {
                list = new List<Model.url_rewrite>();
                string aspPath = Utils.GetMapPath(Path.Combine(webpath, DTKeys.DIRECTORY_REWRITE_MVC, sitePath, "Views"));
                var urls = GetUrls().Urls;
                //是否是继承网站，是则包含继承网站的URL
                string _sitePath = sitePath;
                if (SiteDomains.GetSiteDomains().Inherits.ContainsKey(sitePath))
                {
                    _sitePath = SiteDomains.GetSiteDomains().Inherits[sitePath];
                }
                //获取当前网站URL
                urls = urls.FindAll(p => p.site == string.Empty || p.site == sitePath || p.site == _sitePath);
                //验证是否有效
                foreach (var t in urls)
                {
                    if (t.type == "plugin")
                    {
                        string dirPath = t.inherit.Split('.')[3].ToLower();//插件目录
                        if (File.Exists(aspPath + "/" + "plugins/" + dirPath + "/" + t.page))
                        {
                            list.Add(t);
                        }
                    }
                    else
                    {
                        string path = aspPath + "/" + t.page;
                        path = string.Format(path, 1);
                        if (File.Exists(path))//判断文件是否存在站点中 但是切换专题模板时 替换{0}时访问不了
                        {
                            list.Add(t);
                        }
                        //list.Add(t);
                    }
                }
                if (list.Count > 0)
                {
                    CacheFactory.Cache().WriteCache(list, DTKeys.CACHE_SITE_EFFECTIVE_URLS + "_" + sitePath);
                }
            }
            return list;
        }
    }
    #endregion

    #region 站点绑定域名信息类==================================
    /// <summary>
    /// 域名字典
    /// </summary>
    public class SiteDomains
    {
        private static object lockHelper = new object();
        private static volatile SiteDomains instance = null;
        //默认站点目录
        private string _default_path = string.Empty;
        public string DefaultPath
        {
            get { return _default_path; }
            set { _default_path = value; }
        }
        //站点目录列表
        private Dictionary<string, string> _paths;
        public Dictionary<string, string> Paths
        {
            get { return _paths; }
            set { _paths = value; }
        }
        //继承站点目录列表
        private Dictionary<string, string> _inherits;
        public Dictionary<string, string> Inherits
        {
            get { return _inherits; }
            set { _inherits = value; }
        }
        //站点域名列表
        private Dictionary<string, string> _domains;
        public Dictionary<string, string> Domains
        {
            get { return _domains; }
            set { _domains = value; }
        }
        //所有站点实体
        private List<Model.sites> _sitelist;
        public List<Model.sites> SiteList
        {
            get { return _sitelist; }
            set { _sitelist = value; }
        }
        //构造函数
        public SiteDomains()
        {
            SiteList = new BLL.sites().GetList(0, string.Empty, "sort_id asc,id desc");
            Paths = new Dictionary<string, string>();//所有站点目录
            Domains = new Dictionary<string, string>();//所有独立域名列表
            Inherits = new Dictionary<string, string>(); //所有继承站点列表

            if (SiteList != null)
            {
                foreach (Model.sites modelt in SiteList)
                {
                    //所有站点目录赋值
                    Paths.Add(modelt.build_path, modelt.domain);
                    //所有独立域名列表赋值
                    if (modelt.domain.Length > 0 && !Domains.ContainsKey(modelt.domain))
                    {
                        Domains.Add(modelt.domain, modelt.build_path);
                    }
                    //继承站点赋值
                    if (modelt.inherit_id > 0)
                    {
                        var t = SiteList.Find(p => p.id == modelt.inherit_id);
                        if (t != null)
                        {
                            Inherits.Add(modelt.build_path, t.build_path);
                        }
                    }

                    if (modelt.is_default == 1 && DefaultPath == string.Empty)//默认站点赋值
                    {
                        DefaultPath = modelt.build_path;
                    }
                }
            }
        }
        /// <summary>
        /// 返回域名字典
        /// </summary>
        public static SiteDomains GetSiteDomains()
        {
            SiteDomains _cache = CacheFactory.Cache().GetCache<SiteDomains>(DTKeys.CACHE_SITE_HTTP_DOMAIN);
            lock (lockHelper)
            {
                if (_cache == null)
                {
                    instance = new SiteDomains();
                    CacheFactory.Cache().WriteCache(instance, DTKeys.CACHE_SITE_HTTP_DOMAIN, 10);
                }
            }
            return instance;
        }
    }
    #endregion
}